package jp.aist.gtrc.plus.admission.command;

import java.io.BufferedReader;
import java.io.InputStreamReader;
//import java.io.Serializable;

import java.text.SimpleDateFormat;
import java.util.regex.MatchResult;
import java.util.Scanner;
import java.util.Calendar;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import jp.aist.gtrc.plus.admission.*;
import jp.aist.gtrc.plus.admission.EDF.JobArray;
import jp.aist.gtrc.plus.scheduler.util.TimeUtil;


public class JobSubmitter extends UnicastRemoteObject implements QcommandClient {

        public static final int COMM_DELAY = 15;  // seconds allowed for communication delay
        private static String hostname = "localhost";

        private String file;
        private String array_request;
        private String walltime;
        private String deadline;

        public JobSubmitter(String file, String array_request, String walltime, String deadline) throws RemoteException {
           this.file = file;
           this.array_request = array_request;
           this.walltime = walltime;
           this.deadline = deadline;
        }

        protected static AdmissionServer getAdmissionServer() throws AdmissionException {
           if (System.getSecurityManager() == null) 
              System.setSecurityManager(new AdmissionSecurityManager()); 

           int rmiPort = Registry.REGISTRY_PORT;

           try {
              Registry registry = LocateRegistry.getRegistry(hostname,rmiPort);
//System.out.println(registry);
              AdmissionServer admissionServer = (AdmissionServer) registry.lookup(AdmissionServer.RMI_OBJ_NAME);
              return admissionServer;
           } catch (Exception e) {
              e.printStackTrace();
              throw new AdmissionException("Cannot connect to rmi://" + hostname + ":" + rmiPort);
           }
        } 

   public static void main(String[] args) throws Exception {

      JobSubmitter jobSubmitter = null;
      AdmissionServer admissionServer = null;
      JobArray jobArray = null;
      try {
         String file = args[0];
         String array_request = args[1];
         String walltime = args[2];
         String deadline = args[3];
         if (args.length == 5) {
            hostname = args[4];
         }
        
         jobSubmitter = new JobSubmitter(file,array_request,walltime,deadline); 

         
         int firstJobNum = parseFirstJobNum(array_request);   
         int lastJobNum = parseLastJobNum(array_request);   
         int jobNum = parseArrayRequest(array_request);
         int exeTime = Integer.parseInt(walltime);
         Calendar calendar = TimeUtil.makeCalendarFromPOSIX(deadline);
         if (calendar == null) {
            throw new RuntimeException("Wrong POSIX date time format");
         }
         SimpleDateFormat dateFormater = new SimpleDateFormat("yyyyMMddHHmm");
         String posixDateTime = dateFormater.format(calendar.getTime());
         Calendar currentTime = Calendar.getInstance();
//         int dl = (int) (calendar.getTimeInMillis() - currentTime.getTimeInMillis())/ 1000; 
         int dl = (int) (calendar.getTimeInMillis()/1000); 
         jobArray = new JobArray("NewJob",firstJobNum,lastJobNum,jobNum,exeTime,dl);
      } catch (Exception e) {
         e.printStackTrace();
         usage();
         System.exit(1);
      }


      try {
         admissionServer = getAdmissionServer();
         //admissionServer.submit(jobArray,jobSubmitter);
         int newDeadline = admissionServer.submitAndRecommend(jobArray,jobSubmitter);
         if (newDeadline > 0) {
            Calendar currentTime = Calendar.getInstance();
            currentTime.setTimeInMillis(((long) newDeadline + COMM_DELAY)*1000);
//            currentTime.add(Calendar.SECOND,newDeadline);
            System.err.println("Job cannot be completed by time earlier than " + currentTime.getTime());
         }
         System.exit(0);
         
      } catch (Exception e) {
         //e.printStackTrace();
         System.err.println(e);
         System.exit(1);
      }

   }

   private static int parseArrayRequest(String array_request) throws Exception {
      int num = 0;
      Scanner sc = new Scanner(array_request);
      sc.findInLine("^(\\d+)-(\\d+)$");
      MatchResult result = sc.match();
      if (result.groupCount() == 2) {
         num = Integer.parseInt(result.group(2)) - Integer.parseInt(result.group(1)) + 1;
         if (num <=0) throw new RuntimeException("Negative number of tasks found");
      } else {
         throw new RuntimeException("Wrong format of array request");
      }
      
      return num;
   }

   private static int parseFirstJobNum(String array_request) throws Exception {
      int num = 0;
      Scanner sc = new Scanner(array_request);
      sc.findInLine("^(\\d+)-(\\d+)$");
      MatchResult result = sc.match();
      if (result.groupCount() == 2) {
         num = Integer.parseInt(result.group(1));
         if (num <=0) throw new RuntimeException("Negative start job number found");
      } else {
         throw new RuntimeException("Wrong format of array request");
      }
      
      return num;
   }

   private static int parseLastJobNum(String array_request) throws Exception {
      int num = 0;
      Scanner sc = new Scanner(array_request);
      sc.findInLine("^(\\d+)-(\\d+)$");
      MatchResult result = sc.match();
      if (result.groupCount() == 2) {
         num = Integer.parseInt(result.group(2));
         if (num <=0) throw new RuntimeException("Negative last job number found");
      } else {
         throw new RuntimeException("Wrong format of array request");
      }
      
      return num;
   }
    
        /*
         * @return job id if success, asssuming that only job id is returned 
         *          from qsub.
         */
        public String qcommand() { // callback method
           //String cmd = "qsub -h -t " + array_request + " -l walltime=" + walltime + " -l deadline=" + deadline + " " + file;
           //String cmd = "qsub -h -t " + array_request + " -l walltime=" + walltime + " -l deadline=" + deadline + " -W x=" + args + " " + file;
           String cmd = "qsub -h -t " + array_request + " -W x=" + walltime + " -l deadline=" + deadline + " " + file;
           //System.out.println(cmd);

           String jobId = null;
           try {
               
              Process p = Runtime.getRuntime().exec(cmd);
              BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

              BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

              // read the output from the command
              //System.out.println("Here is the standard output of the command:\n");
              String s = null;
              while ((s = stdInput.readLine()) != null) {
                 if (jobId == null) jobId = s;
                 System.out.println(s);
              }
            
              // read any errors from the attempted command
              //System.out.println("Here is the standard error of the command (if any):\n");
              boolean hasError = false;
              while ((s = stdError.readLine()) != null) {
                 System.err.println(s);
                 hasError = true;
              }
              if (hasError) throw new RuntimeException(s);

           }
           catch (Exception e) {
              //e.printStackTrace();
              throw new RuntimeException(e);
           }
   
           return jobId;
        }

        public String qcommand(String args) { // callback method with arbitrary arguments. Here we consider args as the confirmation deadline in timet

           String jobId = null;
           try {
              int confirmationDeadline = Integer.parseInt(args);
              Calendar calendar = Calendar.getInstance();
              calendar.setTimeInMillis(((long) confirmationDeadline)*1000);

              SimpleDateFormat dateFormater = new SimpleDateFormat("yyyyMMddHHmm.ss");
              String posixDateTime = dateFormater.format(calendar.getTime());

              String cmd = "qsub -h -t " + array_request + " -W x=" + walltime + " -l prologue=qos_prologue.sh,epilogue=qos_epilogue.sh,deadline=" + posixDateTime + " " + file;
              //System.out.println(cmd);
               
              Process p = Runtime.getRuntime().exec(cmd);
              BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

              BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

              // read the output from the command
              //System.out.println("Here is the standard output of the command:\n");
              String s = null;
              while ((s = stdInput.readLine()) != null) {
                 if (jobId == null) {
                    jobId = s;
                    //dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    s = s + " " + posixDateTime;
                 }
                 System.out.println(s);
              }
            
              // read any errors from the attempted command
              //System.out.println("Here is the standard error of the command (if any):\n");
              boolean hasError = false;
              while ((s = stdError.readLine()) != null) {
                 System.err.println(s);
                 hasError = true;
              }
              if (hasError) throw new RuntimeException(s);

           }
           catch (Exception e) {
              //e.printStackTrace();
              throw new RuntimeException(e);
           }
   
           return jobId;
        }

        public static void usage() {
           System.err.println("Usage: classname  <file> <array_request> <time> <deadline> [hostname]");
           System.err.println("      array_request in the form of start-stop");
           System.err.println("      exection time of each task in seconds.");
           System.err.println("      deadline in [[[[CC]YY]MM]DD]hhmm[.SS] format.");
           System.err.println("If deadline is given in a short format and it is not possible with the current time, it is automatically adjusted, e.g. if the current time is 10:00am and the deadline is set as 0900, the deadline will be interpret as 0900 of tomorrow.");
        }

}
